﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
using System.Reflection;

using Microsoft.Practices.ServiceLocation;

using Happy.ExtentionMethods;

namespace Happy.Event.Offline
{
    /// <summary>
    /// 离线事件处理器。
    /// </summary>
    public sealed class OfflineEventProcessor : IOfflineEventProcessor
    {
        private IOfflineEventQueue _offlineEventQueue;

        /// <summary>
        /// 构造方法。
        /// </summary>
        /// <param name="offlineEventQueue">离线事件队列</param>
        public OfflineEventProcessor(IOfflineEventQueue offlineEventQueue)
        {
            offlineEventQueue.MustNotNull("offlineEventQueue");

            _offlineEventQueue = offlineEventQueue;
        }

        /// <inheritdoc />
        public void Start()
        {
            while (true)
            {
                using (var ts = new TransactionScope(TransactionScopeOption.Required))
                {
                    var offlineEvent = _offlineEventQueue.Receive();

                    this.ExecuteEvent(offlineEvent.ToEvent());

                    ts.Complete();
                }
            }
        }

        private void ExecuteEvent(object @event)
        {
            var offlineEventSubscribers = this.GetOfflineEventSubscribers(@event);

            foreach (var offlineEventSubscriber in offlineEventSubscribers)
            {
                var method = offlineEventSubscriber
                    .GetType()
                    .GetMethod("Handle", new Type[] { @event.GetType() });

                if (method == null)
                {
                    method = offlineEventSubscriber
                      .GetType()
                      .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                      .First(x => x.Name.EndsWith(string.Format("IOfflineEventSubscriber<{0}>.Handle", @event.GetType().FullName)));
                }

                method.Invoke(offlineEventSubscriber, new object[] { @event });
            }
        }

        private IEnumerable<object> GetOfflineEventSubscribers(object @event)
        {
            var offlineEventSubscriberType = typeof(IOfflineEventSubscriber<>).MakeGenericType(@event.GetType());

            return ServiceLocator
                .Current
                .GetAllInstances(offlineEventSubscriberType);
        }
    }
}
